package me.thinkjet.sso.server.service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import me.thinkjet.sso.server.Credentials;
import me.thinkjet.sso.server.authentication.Authentication;
import me.thinkjet.sso.server.authentication.AuthenticationManager;
import me.thinkjet.sso.server.authentication.exception.AuthenticationException;
import me.thinkjet.sso.server.authentication.exception.BadCredentialsAuthenticationException;
import me.thinkjet.sso.server.ticket.ServiceTicket;
import me.thinkjet.sso.server.ticket.TicketGrantingTicket;
import me.thinkjet.sso.server.ticket.Registry.ServiceTicketRegistry;
import me.thinkjet.sso.server.ticket.Registry.TicketGrantingTicketRegistry;
import me.thinkjet.sso.server.ticket.exception.InvalidTicketException;
import me.thinkjet.sso.server.ticket.exception.TicketCreationException;
import me.thinkjet.sso.server.ticket.exception.TicketException;
import me.thinkjet.sso.server.ticket.exception.TicketValidationException;
import me.thinkjet.sso.server.utils.StringUtils;
import me.thinkjet.sso.server.utils.UniqueTicketIdGenerator;

/**
 * 
 * @ClassName CentralAuthenticationService
 * @author johnny_zyc
 * @Modified 2013-3-2 下午11:08:16
 *
 */
public final class CentralAuthenticationService {

	private TicketGrantingTicketRegistry ticketGrantingTicketRegistry = new TicketGrantingTicketRegistry();

	private ServiceTicketRegistry serviceTicketRegistry = new ServiceTicketRegistry();

	private AuthenticationManager authenticationManager = new AuthenticationManager();

	private UniqueTicketIdGenerator uniqueTicketIdGenerator = new UniqueTicketIdGenerator();

	public void destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
		if (!StringUtils.hasText(ticketGrantingTicketId)) {
			return;
		}
		final TicketGrantingTicket ticket = (TicketGrantingTicket) this.ticketGrantingTicketRegistry
				.getTicket(ticketGrantingTicketId);

		if (ticket == null) {
			return;
		}

		ticket.expire();
		this.ticketGrantingTicketRegistry.deleteTicket(ticketGrantingTicketId);
	}

	/**
	 * 根据TGT_ID 和 service 生成 ST
	 * 
	 * @param ticketGrantingTicketId
	 * @param service
	 * @param credentials
	 * @return
	 * @throws TicketException
	 */
	public String grantServiceTicket(final String ticketGrantingTicketId,
			final String service, final Credentials credentials)
			throws TicketException {

		if (!(StringUtils.hasText(ticketGrantingTicketId) && StringUtils
				.hasText(service))) {
			throw new InvalidTicketException();
		}
		final TicketGrantingTicket ticketGrantingTicket;
		ticketGrantingTicket = this.ticketGrantingTicketRegistry
				.getTicket(ticketGrantingTicketId);

		if (ticketGrantingTicket == null) {
			throw new InvalidTicketException();
		}

		synchronized (ticketGrantingTicket) {
			if (ticketGrantingTicket.isExpired()) {
				this.ticketGrantingTicketRegistry
						.deleteTicket(ticketGrantingTicketId);
				throw new InvalidTicketException();
			}
		}

		if (credentials != null) {
			try {
				final Authentication authentication = this.authenticationManager
						.authenticate(credentials);
				final Authentication originalAuthentication = ticketGrantingTicket
						.getAuthentication();

				if (!(authentication.getId().equals(
						originalAuthentication.getId()) && authentication
						.getAttributes().equals(
								originalAuthentication.getAttributes()))) {
					throw new TicketCreationException();
				}
			} catch (final AuthenticationException e) {
				throw new TicketCreationException(e);
			}
		}

		final ServiceTicket serviceTicket = ticketGrantingTicket
				.grantServiceTicket(uniqueTicketIdGenerator
						.getNewTicketId(ServiceTicket.PREFIX), service,
						credentials != null);

		this.serviceTicketRegistry.addTicket(serviceTicket);

		return serviceTicket.getId();
	}

	/**
	 * 
	 * @param ticketGrantingTicketId
	 * @param service
	 * @return
	 * @throws TicketException
	 */
	public String grantServiceTicket(final String ticketGrantingTicketId,
			final String service) throws TicketException {
		return this.grantServiceTicket(ticketGrantingTicketId, service, null);
	}

	/**
	 * 为不存在TGT绑定的ST生成唯一TGT
	 * 
	 * @param serviceTicketId
	 * @param credentials
	 * @return
	 * @throws TicketException
	 */
	public String delegateTicketGrantingTicket(final String serviceTicketId,
			final Credentials credentials) throws TicketException {

		if (!(StringUtils.hasText(serviceTicketId) && credentials != null)) {
			throw new InvalidTicketException();
		}

		try {
			final Authentication authentication = this.authenticationManager
					.authenticate(credentials);

			final ServiceTicket serviceTicket;
			serviceTicket = (ServiceTicket) this.serviceTicketRegistry
					.getTicket(serviceTicketId);

			if (serviceTicket == null || serviceTicket.isExpired()) {
				throw new InvalidTicketException();
			}

			final TicketGrantingTicket ticketGrantingTicket = serviceTicket
					.grantTicketGrantingTicket(this.uniqueTicketIdGenerator
							.getNewTicketId(TicketGrantingTicket.PREFIX),
							authentication);

			this.ticketGrantingTicketRegistry.addTicket(ticketGrantingTicket);

			return ticketGrantingTicket.getId();
		} catch (final AuthenticationException e) {
			throw new TicketCreationException(e);
		}
	}

	/**
	 * 验证ST
	 * 
	 * @param serviceTicketId
	 * @param service
	 * @param key 
	 * @return
	 * @throws TicketException
	 */
	public Authentication validateServiceTicket(final String serviceTicketId,
			final String service, String key) throws TicketException {
		//key is not used
		if (!(StringUtils.hasText(serviceTicketId) && StringUtils
				.hasText(service))) {
			throw new InvalidTicketException();
		}

		final ServiceTicket serviceTicket = this.serviceTicketRegistry
				.getTicket(serviceTicketId);

		if (serviceTicket == null) {
			throw new InvalidTicketException();
		}

		try {
			synchronized (serviceTicket) {
				if (serviceTicket.isExpired()) {
					throw new InvalidTicketException();
				}
			}

			final Authentication authentication = serviceTicket
					.getGrantingTicket().getAuthentication();
			if (serviceTicket.getService().equals(service)) {
				return authentication;
			} else {
				throw new TicketValidationException(serviceTicket.getService());
			}
		} finally {
			this.serviceTicketRegistry.deleteTicket(serviceTicketId);
		}
	}

	/**
	 * 验证登录信息，生成TGT，并保存，返回TGT_ID
	 * 
	 * @param credentials
	 * @return
	 * @throws TicketCreationException
	 */
	public String createTicketGrantingTicket(final Credentials credentials)
			throws TicketCreationException {
		if (credentials == null) {
			throw new TicketCreationException(
					new BadCredentialsAuthenticationException());
		} else {
			try {
				final Authentication authentication = this.authenticationManager
						.authenticate(credentials);

				final TicketGrantingTicket ticketGrantingTicket = new TicketGrantingTicket(
						this.uniqueTicketIdGenerator
								.getNewTicketId(TicketGrantingTicket.PREFIX),
						authentication, credentials.isRememberme());

				this.ticketGrantingTicketRegistry
						.addTicket(ticketGrantingTicket);
				return ticketGrantingTicket.getId();
			} catch (final AuthenticationException e) {
				throw new TicketCreationException(e);
			}
		}
	}

	protected void LogOut(final HttpServletRequest request,
			final HttpServletResponse response) throws Exception {
		CookieService cookieService = new CookieService(
				TicketGrantingTicket.COOKIE_NAME);
		final String ticketGrantingTicketId = cookieService
				.getCookieValue(request);
		if (ticketGrantingTicketId != null) {
			this.destroyTicketGrantingTicket(ticketGrantingTicketId);
			cookieService.removeCookie(response);
		}

	}
}
